#include "ITutorial01.h"
#include "StreamListener.h"

#include <math.h>

static float rotation = 0.0;
static int pauseAnimate = 1;
static int pauseMode = 0;
static int pauseFrames = 0;

//-------------------------------------------------------------------------------------
ITutorial01::ITutorial01(float initialTime, char *filename) {
  timePostition = initialTime;
  dumpfile = filename;
}
//-------------------------------------------------------------------------------------
ITutorial01::~ITutorial01(void) {
}

//-------------------------------------------------------------------------------------
void ITutorial01::createScene(void) {
  mSceneMgr->setAmbientLight(Ogre::ColourValue(1.0f, 1.0f, 1.0f));
  mEntity = mSceneMgr->createEntity("Robot", "robot.mesh");
  // Create the scene node
  mNode = mSceneMgr->getRootSceneNode()->
          createChildSceneNode("RobotNode", Ogre::Vector3(0.0f, 0.0f, 25.0f));
  mNode->attachObject(mEntity);

  // Create the walking list
  mWalkList.push_back(Ogre::Vector3(550.0f,  0.0f,  50.0f));
  mWalkList.push_back(Ogre::Vector3(-100.0f,  0.0f, -200.0f));
  mWalkList.push_back(Ogre::Vector3(0.0f,  0.0f, 25.0f));
  mWalkList.push_back(Ogre::Vector3(550.0f,  0.0f,  50.0f));
  mWalkList.push_back(Ogre::Vector3(-100.0f,  0.0f, -200.0f));
  mWalkList.push_back(Ogre::Vector3(0.0f,  0.0f, 25.0f));

  // Create objects so we can see movement
  Ogre::Entity *ent;
  Ogre::SceneNode *node;

  ent = mSceneMgr->createEntity("Knot1", "knot.mesh");
  node = mSceneMgr->getRootSceneNode()->createChildSceneNode("Knot1Node",
         Ogre::Vector3(0.0f, -10.0f,  25.0f));
  node->attachObject(ent);
  node->setScale(0.1f, 0.1f, 0.1f);

  ent = mSceneMgr->createEntity("Knot2", "knot.mesh");
  node = mSceneMgr->getRootSceneNode()->createChildSceneNode("Knot2Node",
         Ogre::Vector3(550.0f, -10.0f,  50.0f));
  node->attachObject(ent);
  node->setScale(0.1f, 0.1f, 0.1f);

  ent = mSceneMgr->createEntity("Knot3", "knot.mesh");
  node = mSceneMgr->getRootSceneNode()->createChildSceneNode("Knot3Node",
         Ogre::Vector3(-100.0f, -10.0f,-200.0f));
  node->attachObject(ent);
  node->setScale(0.1f, 0.1f, 0.1f);

  setupCamera(timePostition);

  listener = new StreamListener(mWindow, mCamera, dumpfile);
}

#define PI (3.141592653589793)

void ITutorial01::setupCamera(float time) {
  float angle = time * (PI/4.0);
  float camX = sinf(angle) * 640 + 260;
  float camY = cosf(angle) * 640 - 50;
  mCamera->setPosition(camX, 240.0f, camY);
  mCamera->lookAt(240.0f, 0.0f, -50.0f);
}

void ITutorial01::createFrameListener(void) {
  BaseApplication::createFrameListener();

  // Set idle animation
  mAnimationState = mEntity->getAnimationState("Idle");
  mAnimationState->setLoop(true);
  mAnimationState->setEnabled(true);

  // Set default values for variables
  mWalkSpeed = 35.0f;
  mDirection = Ogre::Vector3::ZERO;
}

bool ITutorial01::nextLocation(void) {
  if (mWalkList.empty()) {
    mShutDown = true;
    return false;
  }
  mDestination = mWalkList.front();  // this gets the front of the deque
  mWalkList.pop_front();             // this removes the front of the deque

  mDirection = mDestination - mNode->getPosition();
  mDistance = mDirection.normalise();
  return true;
}

#define FIXED_TIME 0.04 // 25 FPS

bool ITutorial01::frameRenderingQueued(const Ogre::FrameEvent &evt) {
  for (int i = 0; i < pauseAnimate; ++i) {
  if (mDirection == Ogre::Vector3::ZERO) {
    if (nextLocation()) {
      // Set walking animation
      mAnimationState = mEntity->getAnimationState("Walk");
      mAnimationState->setLoop(true);
      mAnimationState->setEnabled(true);
    }
  } else {
    Ogre::Real move = mWalkSpeed * FIXED_TIME;
    mDistance -= move;
    if (mDistance <= 0.0f) {
      mNode->setPosition(mDestination);
      mDirection = Ogre::Vector3::ZERO;
      // Set animation based on if the robot has another point to walk to.
      if (! nextLocation()) {
        // Set Idle animation
        mAnimationState = mEntity->getAnimationState("Idle");
        mAnimationState->setLoop(true);
        mAnimationState->setEnabled(true);
      } else {
        Ogre::Vector3 src = mNode->getOrientation() * Ogre::Vector3::UNIT_X;
        if ((1.0f + src.dotProduct(mDirection)) < 0.0001f) {
          mNode->yaw(Ogre::Degree(180));
        } else {
          Ogre::Quaternion quat = src.getRotationTo(mDirection);
          mNode->rotate(quat);
        } // else

      }
    } else {
      mNode->translate(mDirection * move);
    } // else
  } // if
  mAnimationState->addTime(FIXED_TIME);
  } // for

  // Set the camera to look at our handiwork
  timePostition += FIXED_TIME * rotation;
  setupCamera(timePostition);
  if (pauseMode) {
      if (++pauseMode > pauseFrames) {
        pauseAnimate = pauseFrames;
        pauseMode = 1;
      } else {
        pauseAnimate = 0;
      }
  }
  return BaseApplication::frameRenderingQueued(evt);
}

int main(int argc, char *argv[])  {
  float timePos = 0.0;
  char *filename = "stream.dump";
  if (argc > 1) {
    filename = argv[1];
  }
  if (argc > 2) {
    timePos = atof(argv[2]);
  }
  if (argc > 3) {
    rotation = atof(argv[3]);
  }
  if (argc > 4) {
    pauseMode = 1;
    pauseAnimate = atoi(argv[4]);
    if (rotation > 0) {
      pauseFrames = 8*25 / rotation;
    } else {
      pauseFrames = -8*25 / rotation;
    }
  }

   // Create application object
  ITutorial01 app(timePos, filename);

  try {
    app.go();
  } catch(Ogre::Exception& e) {
    std::cerr << "An exception has occured: " <<
              e.getFullDescription().c_str() << std::endl;
  }

  return 0;
}

